home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / science / ack3d.zip / NOTES.TXT < prev    next >
Text File  |  1994-01-09  |  32KB  |  825 lines

  1.                   ACK-3D
  2.          ( Animation Construction Kit 3D )
  3.  
  4.  
  5. ******************************************************************************
  6.     This file is included for completeness. It was originally distributed with
  7. the earlier version of the ACK engine and contains some of the technical notes
  8. used in the ray-casting technique. This file has not been edited for the new
  9. engine so some of the information may not apply. In general however, it gives
  10. a basic foundation for creation of one type of ray-casting engine.
  11. ******************************************************************************
  12.  
  13.  
  14.                 Disclaimer
  15.  
  16.      The author, Lary Myers, and any other persons referred to
  17. in this documentation or in the computer programs ACK3D and MAPEDIT
  18. accept no responsibility for any loss of time, money or productivity,
  19. or damage to any person(s) or computer hardware or software, as a
  20. result of using the programs ACK3D and MAPEDIT, even if the above
  21. mentioned had knowledge or had been notified of the possibilities
  22. of such events.
  23.  
  24.  
  25.  
  26.                Distribution and Usage
  27.  
  28.      Considerable thought has gone into just how the source code for ACK3D
  29. would be released. Some possibilities included providing only the construction
  30. kit and selling the source to interested parties, or providing the source to
  31. be used as freeware for private use but requiring license for commercial ventures,
  32. and so forth.
  33.  
  34.      What I've decided is to release all the source, header files, notes, etc
  35. into the public domain as "PUBLICWARE". You may freely use the engine source
  36. and associated files for the programs ACK3D and MAPEDIT anyway you see fit.
  37. One of the goals when I started this project was to place the means into other
  38. peoples hands, more talented than I, so they could then create worlds of
  39. adventure and entertainment for the rest of us to share in.
  40.  
  41.      Here is the one reservation that I must insist on.
  42.  
  43.      The author, Lary Myers, hereby grants the right to use, modify, and sell
  44. the source code provided in the ACK3D collection of files, with the stipulation
  45. that Lary Myers accepts no responsibilty for changes desired in the code, bug
  46. fixes, or future updates.
  47.  
  48.  
  49.      Now, with all that aside, PLEASE let me know of any comments and/or
  50. suggestions you may have regarding possible uses for the engine and any
  51. enhancements you wish to share with me and others. The entire project is
  52. still far from over and together maybe we can create a powerful engine for
  53. use in a wide variety of applications.
  54.  
  55.  
  56.  
  57. ------------------------------------------------------------------------------
  58. Programming notes:
  59.  
  60. Author: Lary Myers
  61.  
  62.  
  63. Development system: 486DX 33Mhz
  64.             8 Meg RAM
  65.             210 Meg HD
  66.             Boca VGA
  67.             Microsoft mouse
  68.  
  69.             Borland C++ Version 3.1
  70.             Microsoft Macro Assembler Version 5.00A
  71.  
  72. Languages used:        C and Assembler
  73.  
  74. Model used:        Compact
  75.  
  76.  
  77. ------------------------------------------------------------------------------
  78. Compiling the engine:
  79.  
  80.     1. Place all of the files from the .ZIP file into one directory. Since I
  81.        use Borland I opted for the following directory structure;
  82.  
  83.     \BORLANDC\ACK3D\SRC    <- Contains all the source for the engine
  84.  
  85.     \BORLANDC\ACK3D\KIT    <- Contains executables, images, for the kit
  86.  
  87.     2. Several years ago I wrote a program called MK.EXE which was used to
  88.        MAKE programs based on dependencies, etc. Even though the compilers
  89.        nowadays have much better makes, I still use this old one of mine, so
  90.        the .MAK files provided with the engine are based on using it with
  91.        MK.EXE. You can either use them as is (if you have BorlandC) or use
  92.        them to see what's needed to create a standard MAKE file. If you use
  93.        MK then type in the following to begin the process;
  94.  
  95.         MK A   <- This will build the ACK3D engine
  96.  
  97.         MK M   <- This will build the Map Editor
  98.  
  99.  
  100.  
  101. ------------------------------------------------------------------------------
  102. Goals:
  103.     1. Create a 3D graphic engine which visually mimics the Wolfenstein
  104.        3D game as a starting point, then add new features and interface.
  105.  
  106.     2. Use the engine to create a generic construction kit.
  107.  
  108.     3. Provide the source and construction kit to the general public.
  109.  
  110.  
  111. Techniques:
  112.  
  113.     The graphic engine is based on a technique called Ray Casting, a term
  114. that has been used to describe the process where a 2D representation of a
  115. maze is rendered in 3D. The details of Ray Casting are provided below;
  116.  
  117.     1. Using a two dimensional map similiar to graph paper, a maze is built
  118.        consisting of borders (walls) in both the X and Y planes. These walls
  119.        describe the physical boundaries of the map. Figure 1 shows an example
  120.        of a simple map.
  121.  
  122.  
  123.      X
  124.     ╔════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╗
  125.       Y ║....║....║....║....║....║....║....║....║....║....║....║....║
  126.     ║....║....║....║....║....║....║....║....║....║....║....║....║
  127.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  128.     ║....║      ║    ║    ║     ║    ║       ║    ║    ║      ║    ║....║
  129.     ║....║      ║    ║    ║     ║    ║       ║    ║    ║      ║    ║....║
  130.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  131.     ║....║      ║    ║....║....║....║....║....║    ║      ║    ║....║
  132.     ║....║      ║    ║....║....║....║....║....║    ║      ║    ║....║
  133.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  134.     ║....║      ║    ║....║     ║    ║       ║....║    ║      ║    ║....║
  135.     ║....║      ║    ║....║     ║    ║       ║....║    ║      ║    ║....║
  136.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  137.     ║....║      ║    ║    ║     ║    ║       ║....║    ║      ║    ║....║
  138.     ║....║      ║    ║    ║     ║    ║       ║....║    ║      ║    ║....║
  139.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  140.     ║....║      ║    ║....║     ║    ║       ║....║....║....║....║....║
  141.     ║....║      ║    ║....║     ║    ║       ║....║....║....║....║....║
  142.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  143.     ║....║      ║    ║....║     ║    ║       ║    ║    ║      ║    ║....║
  144.     ║....║      ║    ║....║     ║    ║       ║    ║    ║      ║    ║....║
  145.     ╠════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╬════╣
  146.     ║....║....║....║....║....║....║....║....║....║....║....║....║
  147.     ║....║....║....║....║....║....║....║....║....║....║....║....║
  148.     ╚════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╝
  149.  
  150.  
  151.     ╔════╗
  152.     ║....║    Indicates a square that has walls
  153.     ║....║
  154.     ╚════╝
  155.  
  156.     ╔════╗
  157.     ║    ║    Indicates a blank square
  158.     ║    ║
  159.     ╚════╝
  160.  
  161.  
  162.             Figure 1
  163.  
  164.  
  165.  
  166.  
  167.     2. The map is made up of cubes, each having a fixed size. For our
  168.        purposes we'll use 64x64 units for each square. This allows an
  169.        object (or player) to move 64 units in either the X or Y direction
  170.        before moving into another square. The total map is made up of
  171.        a number of squares to form rows and a number of rows to form a
  172.        two dimensional array (again, just like graph paper has some number
  173.        or sqaures by some number of rows of squares).
  174.  
  175.  
  176.     3. A player is then defined as a location on the map having three
  177.        properties, an X and Y coordinate as well as an Angle that the
  178.        player is currently facing. Figure 2 shows this relationship.
  179.  
  180.  
  181.  
  182.     ╔════╦════╦════╦════╦
  183.     ║....║....║....║....║
  184.     ║....║....║....║....║        P - The current location of the player
  185.     ╠════╬════╬════╬════╬
  186.     ║....║      ║    ║    ║        ->    The current angle the player is facing
  187.     ║....║ P->║    ║    ║
  188.     ╠════╬════╬════╬════╬
  189.     ║....║      ║    ║....║
  190.     ║....║      ║    ║....║
  191.     ╠════╬════╬════╬════╬
  192.     ║....║      ║    ║....║
  193.     ║....║      ║    ║....║
  194.     ╠════╬════╬════╬════╬
  195.  
  196.  
  197.             Figure 2
  198.  
  199.  
  200.  
  201.  
  202.     4. Once we know where the Player is and the current angle the player is
  203.        facing, we can begin to determine what will be visible at that moment
  204.        in time. To begin with, we need to decide on the field of vision that
  205.        will be used. ACK3D uses a 60 degree field of vision to show the
  206.        walls in a realistic setting. This means that the player will see all
  207.        items 30 degrees to the left of the current viewing angle to 30 degrees
  208.        to the right of the current angle. Figure 3 illustrates this.
  209.  
  210.  
  211.  
  212.             /
  213.               /       -30 degrees from viewing angle (VA - 30)
  214.             /
  215.           /
  216.         P ------------->  Current viewing angle (VA)
  217.           \
  218.             \
  219.               \       +30 degrees from viewing angle (VA + 30)
  220.             \
  221.  
  222.  
  223.             Figure 3
  224.  
  225.  
  226.  
  227.  
  228.  
  229.     5. As you can see, if we superimpose Figure 3 onto figure 2 we begin to
  230.        get a field of vision which encompasses those walls in front of the
  231.        player (at the current viewing angle). This gives us Figure 4.
  232.  
  233.  
  234.  
  235.       0    1    2     3
  236.     ╔════╦════╦════╦════╦
  237.       0 ║....║....║.../║....║
  238.     ║....║....║./..║....║        P - The current location of the player
  239.     ╠════╬════/════╬════╬
  240.       1 ║....║    / ║    ║    ║        ->    The current angle the player is facing
  241.     ║....║ P-------->   ║
  242.     ╠════╬══\═╬════╬════╬
  243.       2 ║....║      \    ║....║
  244.     ║....║      ║ \  ║....║
  245.     ╠════╬════╬═══\╬════╬
  246.       3 ║....║      ║    ║....║
  247.     ║....║      ║    ║....║
  248.     ╠════╬════╬════╬════╬
  249.  
  250.  
  251.             Figure 4
  252.  
  253.  
  254.  
  255.     6. What the user (player) should see at this point is shown in Figure 5,
  256.        where the double border represents the screen with the walls displayed
  257.        inside.
  258.  
  259.  
  260.  
  261.  
  262.         ╔═══════════════════════════════════╗
  263.         ║                      / ║
  264.         ║                    /    ║
  265.         ║                  /    ║
  266.         ║┌───────────────────────────┐    ║
  267.         ║│                 │    ║
  268.         ║│                 │    ║
  269.         ║│                 │    ║
  270.         ║│                 │    ║
  271.         ║│                 │    ║
  272.         ║│                 │    ║
  273.         ║│                 │    ║
  274.         ║│                 │    ║
  275.         ║└───────────────────────────┘    ║
  276.         ║                  \    ║
  277.         ║                    \    ║
  278.         ║                      \ ║
  279.         ╚═══════════════════════════════════╝
  280.  
  281.  
  282.             Figure 5
  283.  
  284.  
  285.  
  286.     7. So, how do we get from a flat 2 dimensional map to the appearance of
  287.        3D walls? By calculating the height of the walls as a function of
  288.        distance. Given the current X,Y coordinates of the player and the
  289.        X,Y coordinates of the wall, we can use trigonometry to figure out
  290.        the distance between the player and the wall. The real trick is to
  291.        perform these calculations with the speed necessary to show realistic
  292.        movement in our 3D environment. We'll get into that in a moment. Right
  293.        now, lets look at the brute force method of determining how far the
  294.        walls are. The first thing to remember is that we will be casting a
  295.        ray for each column of the screen. Using VGA mode 13h this gives us
  296.        320 columns by 200 rows. So, 320 rays will be cast out to fill the
  297.        screen with our field of view.
  298.  
  299.  
  300.  
  301.     8. Lets begin by setting some initial values for the player. We said in
  302.        section 2 that each square was 64 by 64 units in size. Looking at
  303.        Figure 4 we see that the player is in square 1,1 about 3/4 the way
  304.        down from the top of the square and about 1/4 the way over from the
  305.        right side of the square. This puts our player X,Y at roughly 80,112.
  306.        The current viewing angle will be 0 for this part of the discussion.
  307.  
  308.        Since we are going to use the trig functions provided in the C libraries,
  309.        we can draw a coordinate system where the angle increases in a clockwise
  310.        direction, giving us Figure 6;
  311.  
  312.  
  313.                    270
  314.                 │    330
  315.                 │  /
  316.                 │/
  317.                180 ─────O───── 0
  318.                 │\
  319.                 │  \
  320.                 │    30
  321.                    90
  322.  
  323.             Figure 6
  324.  
  325.  
  326.     9. Therefore, as mentioned in section 4, we begin our cast 30 degrees to
  327.        the left of the current view angle, or at 330 degrees, and continue
  328.        until we are 30 degrees to the right of the view angle, which will be
  329.        at +30 degrees. As you can see, we can form a right triangle from the
  330.        player position to the point at angle 330.
  331.  
  332.  
  333.                                Opp
  334.                         Tan =  ---
  335.                 /│               Adj
  336.               Hyp /     │
  337.                 /     │Opp               Adj
  338.               / Angle│        Cos =  ---
  339.             P────────┘               Hyp
  340.                 Adj
  341.  
  342.                                Opp
  343.                         Sin =  ---
  344.                                Hyp
  345.             Figure 7
  346.  
  347.  
  348.  
  349.    10. What we want to do at this point is draw an imaginary line from the
  350.        player out to some point at angle 330. But how far do we draw the
  351.        line? As far as needed to encounter a wall or until we leave the outside
  352.        boundaries of the map. Lets say our map is 10 squares across by 8
  353.        rows down. That gives us 80 squares total. The maximum width of our
  354.        map is therefore 64 units times 10 or 640 units, by 64 units times 8
  355.        rows or 512 units. The maximum distance we ever have to look is given
  356.        be the equation;
  357.  
  358.  
  359.  
  360.         c2 = a2 + b2     (c squared = a squared plus b squared)
  361.  
  362.             or
  363.  
  364.         c2 = 640 squared plus 512 squared
  365.  
  366.  
  367.         which gives 819.5999 or 820 units
  368.  
  369.  
  370.             Equation 1
  371.  
  372.  
  373.  
  374.       So, 820 units is how far we'll cast each ray, checking along the way
  375.       for a wall or a boundary condition. We can use the well established
  376.       Bresenham's line drawing algorithm to plot each point along the line
  377.       to see what it strikes. We begin by determining the endpoints of the
  378.       line. The equations for a point on a circle gives us the X and Y
  379.       points we need;
  380.  
  381.  
  382.         X1 = X + (cos(angle) * distance)
  383.  
  384.         Y1 = Y + (sin(angle) * distance)
  385.  
  386.  
  387.  
  388.             Equation 2
  389.  
  390.  
  391.       Using the numbers from section 8 and section 10 we get the following;
  392.  
  393.  
  394.         X1 = 80 + (cos(330) * 820)
  395.  
  396.         Y1 = 112 + (sin(330) * 820)
  397.  
  398.  
  399.         which gives;
  400.  
  401.         X1 = 790
  402.  
  403.         Y1 = -298
  404.  
  405.  
  406.       Passing the coordinates 80,112,790,-298 into a line routine we begin
  407.       to check for intercepts with walls. At each point along the line we
  408.       calculate our map position with the following equation;
  409.  
  410.  
  411.  
  412.         MapPosn =  ( Y / 64 ) * 10 + ( X / 64 )
  413.  
  414.  
  415.     Recalling that our map was 10 squares across and each square was 64
  416.     by 64 units in dimension.
  417.  
  418.  
  419.       Looking back at Figure 4 we see that our first intercept will come when
  420.       the line passes into square 2,0. At this very point we have struck a
  421.       wall and can then determine the distance to that wall. We'll call the
  422.       point Wx and Wy to distinguish them from our line endpoints. Using
  423.       Equation 1 again we can determine the distance to the wall;
  424.  
  425.  
  426.         c2 = a2 + b2
  427.  
  428.  
  429.         c2 = (Wx - x) squared plus (Wy - y) squared
  430.  
  431.  
  432.             Equation 2
  433.  
  434.  
  435.  
  436.       From the drawing scale in Figure 4, it appears a wall is encounterd
  437.       at coordinates 128,128 in square 2,0, so we can use numbers in the
  438.       above to get;
  439.  
  440.  
  441.         c2 = (128 - 80) squared plus (128 - 112) squared
  442.  
  443.  
  444.         which gives us;
  445.  
  446.  
  447.         Distance = 50.596 units or 51 units rounded up
  448.  
  449.  
  450.       Using this distance we can look up the height from a precalculated table
  451.       to see how high the wall should appear on the screen.
  452.  
  453.  
  454.  
  455.       We continue this process for each column of the screen until we have
  456.       a 320 columns of graphic walls displayed, each column showing the walls
  457.       at a certain height, which gives the illusion of distance.
  458.  
  459.  
  460.       As you may have already surmised, we don't advance our ray casting by
  461.       whole degree increments, instead we move in 60/320 degrees or 0.1875
  462.       increments to give us a 60 degree field of view in 320 columns of
  463.       the video screen.
  464.  
  465.  
  466.       This method was initially used by the ACK3D engine and can be summarized
  467.       with the following;
  468.  
  469.  
  470.     a. Take the current viewing angle of the player and subtract 30 from
  471.        it to get the cast angle.
  472.  
  473.     b. Calculate an X and Y endpoint using the players X,Y coordinates,
  474.        the cast angle, and a maximum distance to cast the ray.
  475.  
  476.     c. Using Bresenham's line drawing algorithm, plot each point along the
  477.        line and look for an intercept with a wall or the point where the
  478.        line goes outside of the map boundaries. If a wall is found, return
  479.        the Wx and Wy point of the wall intercept.
  480.  
  481.     d. Using the players X,Y coordinates, and the Wall Wx,Wy coordinates,
  482.        calculate the distance to the wall using Equation 2.
  483.  
  484.     e. Use the distance to look up in a table to get the height of the
  485.        wall to display.
  486.  
  487.     f. Add 0.1875 to the cast angle and go back to step b until 320 rays
  488.        have been cast.
  489.  
  490.  
  491.             Method 1
  492.  
  493.  
  494.  
  495.  
  496.    11. The above discussion was presented in order to give the reader some
  497.        background into the train of thought for ray casting. While Method 1
  498.        did work and did produce a semi-reasonable display, some major problems
  499.        were seen, these can be summarized as;
  500.  
  501.  
  502.     a. Speed - Casting a line along every point for 320 rays was just
  503.            plain SLOW! The screen could actually be seen updating
  504.            from left to right as the rays were cast! (This was without
  505.            any optimizations in the ray casting routines).
  506.  
  507.  
  508.     b. Accuracy - Floating point was used in order to get the needed
  509.               increments in angles and distance, but a problem with
  510.               roundoff still occurred. Some of this was attributable
  511.               to the code (This was pointed out to me by those who
  512.               looked at the routines - my thanks to you all!). But,
  513.               some of it was due to the method itself.
  514.  
  515.  
  516.     c. Intercepts - There was no easy way to determine if we had hit a
  517.             wall on the X or Y borders of the square. One method
  518.             that could have been used was to determine if the
  519.             Wx and Wy points fell on an even 64 boundary, but
  520.             this was never tried. Once we found the correct
  521.             intercept, there was still the problem of determining
  522.             which column of the wall should be drawn.
  523.  
  524.  
  525.  
  526. ------------------------------------------------------------------------------
  527.  
  528. Optimized Ray Casting:
  529.  
  530.  
  531.  
  532.     1. Once it was determined that the method was viable, albiet with its
  533.        problems, a more advanced or optimized method could be tried. This
  534.        is how ACK3D evolved.
  535.  
  536.     2. Many of the givens could be carried forward into the new version, we
  537.        would still use squares on a map, each square being 64 units wide by
  538.        64 units tall. The player properties could still be used, giving us
  539.        an X,Y coordinate pair and a current angle that the player is facing.
  540.        The total viewing angle could remain at 60 degrees with a sweep from
  541.        -30 degrees to the left to +30 degrees to the right of the current
  542.        player angle.
  543.  
  544.     3. The first order of business was to get rid of the floating point. By
  545.        using fixed point numbers we could attain a speedup in the overall
  546.        process. Fixed point numbers are beyond this discussion but essentially
  547.        a fixed point number is where a given number is shifted a certain
  548.        amount to the left to retain the fractional part of the number. This
  549.        new value is then used in a calculation and the result is shifted back
  550.        to the right to remove the fractional part. ACK3D uses two fixed point
  551.        ranges to perform its calculations, the first being a 16 bit shift and
  552.        the second being a 20 bit shift (which we'll get into soon).
  553.  
  554.     4. The next thing was to begin using tables for most of the housekeeping
  555.        calculations. Since we knew that our viewing angles were in 0.1875
  556.        degree increments we could precalculate sine and cosine tables (as well
  557.        as tangent) ahead of time. Our full circle would therefore become
  558.        360 / 0.1875 or 1920 entries in the tables. Every angle from 0 to 360
  559.        was calculated, multiplied by 65536 (our 16 bit shift for fixed point)
  560.        and written out to a file. Now if we needed to find the cosine of
  561.        330 degrees, we could use 330/0.1875 or 1760 to lookup in the cosine
  562.        table and get the value 56759 (after rounding). The same holds true
  563.        for sine and tangent tables (and some others) as well.
  564.  
  565.     5. Now, how to speed up drawing an imaginary line from the player to a
  566.        point off in the distance? Looking at the map it becomes apparent that
  567.        each square is a fixed distance from each other. That is where the
  568.        speedup can be found. Lets break the process down further in that
  569.        we only want to look for walls that fall on the X side and then walls
  570.        that fall on the Y side of the square. This means we have to cast
  571.        two rays for every angle, but it also means we know in advance which
  572.        intercept will occur.
  573.  
  574.  
  575.  
  576.       0    1    2     3
  577.     ╓────╥────╥────B────╥
  578.       0 ║....║....║.../║....║
  579.     ║....║....║./..║....║        P - The current location of the player
  580.     ╟────╫────A────╫────╫
  581.       1 ║....║    / ║    ║    ║        ->    The current angle the player is facing
  582.     ║....║ P-------->   ║
  583.     ╟────╫──\─╫────╫────╫
  584.       2 ║....║      C    ║....║
  585.     ║....║      ║ \  ║....║        ║    Walls on the X side
  586.     ╟────╫────╫───\╫────╫
  587.       3 ║....║      ║    D....║        ─    Walls on the Y side
  588.     ║....║      ║    ║....║
  589.     ╟────╫────╫────╫────╫
  590.  
  591.  
  592.             Figure 8
  593.  
  594.  
  595.  
  596.  
  597.     6. Figure 8 shows our section of the map with only the X walls given as
  598.        double lines, the Y walls (which we'll ignore for now) all fall on the
  599.        single lines. From this we can see that a ray looking only for X walls
  600.        will intercept at points A and B at angle 330 (The scale and angles
  601.        are not precise due to text mode limitations, for the sake of discussion
  602.        lets assume that the angle is 330). We also see that points C and D will
  603.        be struck at our rightmost angle of 30 degrees. This gives us a method
  604.        to optimize the ray casting algorithm into the following;
  605.  
  606.     a. Subtract 30 degrees from the current player angle to get the
  607.        viewing angle.
  608.  
  609.  
  610.     b. Determine the Y intercept of the current square based on the view
  611.        angle and the X coordinate (note that the X coordinate will be the
  612.        right side of the current square if our view angle falls between
  613.        270 degrees and 90 degrees and it will be the left side of the
  614.        current square if our view angle falls between 90 degrees and
  615.        270 degrees). This X,Y intercept of the current square will be
  616.        the first point we look for a wall.
  617.  
  618.        By using the tangent or inverse tangent of the view angle we can
  619.        calculate the Y or X offset depending on which ray we are casting.
  620.        This gives us a starting point in the current square to begin
  621.        looking for walls.
  622.  
  623.        Knowing the width of each square is 64 we determine the next
  624.        Y coordinate that our ray will fall on if we increase (or decrease)
  625.        the X intercept by 64. This can be pre-calculated and placed in a
  626.        table. We continue stepping both the X and Y coordinates with
  627.        addition until a wall is hit or we leave the boundaries of the map.
  628.        By using Equation 1 again we see that a maximum of 13 (rounded)
  629.        points along any given line is all we will ever need to traverse
  630.        the maximum distance in the map. Thats 13 points verses 820
  631.        from the previous method!
  632.  
  633.  
  634.     c. Once a wall is struck we use the Y coordinate to determine what
  635.        column of the wall to display (Y AND 63) and the X coodinate to
  636.        determine the distance (WITHOUT USING SQUARE ROOT).
  637.  
  638.  
  639.     d. We flip the process around and perform a ray cast for walls that
  640.        fall along the Y side of the square, using the X coordinate to
  641.        determine the column of the wall and Y coordinate to determine
  642.        the distance.
  643.  
  644.  
  645.     e. Comparing the distance from the Xray and the distance from the
  646.        yRay for whichever is closer we get the actual wall and wall
  647.        column to display.
  648.  
  649.  
  650.     f. Next, we advance our view angle by one unit in our 1920 entry
  651.        circle and loop back to step b until 320 ray pairs have been
  652.        cast.
  653.  
  654. ------------------------------------------------------------------------------
  655. Determining the height of the bitmap:
  656.  
  657.     1. Once we have the distance to the wall we need to look up the display
  658.        height of the bitmap from our table. This table was pre-calculated
  659.        and is based on dividing the distance into an experimentally derived
  660.        number (that means I tried a variety of values until one looked the
  661.        best!). For ACK3D this turned out to be 18000. All distances from
  662.        1 to our max distance are divided into this number to get a height
  663.        value to draw the bitmap. Heights which are too big or too small are
  664.        set to the maximum or minimum values respectively.
  665.  
  666.  
  667. ------------------------------------------------------------------------------
  668. Objects within our world:
  669.  
  670.     1. Dealing with objects turned out to be a completely different problem.
  671.        One that wasn't apparent from the effort put into the wall algorithm.
  672.        The goal was to present an object to the user that did not show the
  673.        same effects with angles as the walls, in other words, I didn't want
  674.        objects to have corners when looking at them diaganally. This meant
  675.        the distance to the object should not be dependent on either the X or
  676.        the Y ray but should be based on a line of sight ray cast from the
  677.        players current position.
  678.  
  679.     2. After much experimenting and head scratching it was decided to treat
  680.        objects using more traditional 3D methods. But, the speed still had
  681.        to be considered!
  682.  
  683.     3. Again, the brute force method would be to have all of the objects X
  684.        and Y coordinates in a table. Each object is then translated to base
  685.        0,0 from the player and rotated into the field of vision. If the final
  686.        X,Y location of the object fell within the 60 degree field of vision
  687.        then the object would be visible and handled by the drawing routines.
  688.        But what if there were many objects in this table? The time expended
  689.        to check each object soon becomes a function of the number of objects
  690.        that exist. No, some faster way had to be found.
  691.  
  692.     4. So, by combining the ray casting with the process in section 3 above
  693.        we could eliminate all objects not within the current field of vision
  694.        thereby reducing the amount of calculations needed.
  695.        This is the process;
  696.  
  697.     a. During the ray casting process described in section 6 above, also
  698.        look in the object map to see if an object exist. If so, add the
  699.        object to a list and set a flag saying the object has been seen.
  700.        This flag prevents other near-angle rays from adding the same
  701.        object to the list, only one entry is needed.
  702.  
  703.     b. Sort the object in the list with nearer objects being pushed down
  704.        and farther objects being near the top. This way when the objects
  705.        are later drawn the closer ones will hide objects that may be
  706.        farther away and at the same angle.
  707.  
  708.     c. Continue the ray casting past the object just found. This allows
  709.        us to find additional objects and finally any walls that may be
  710.        in the line of sight. What gets returned to the caller of the
  711.        ray casting routine is always the wall information, never any
  712.        objects (since objects are stored in a global list).
  713.  
  714.     d. Once all the rays for the entire screen are cast we then draw the
  715.        walls into the off-screen video buffer. Now its time to check for
  716.        the objects.
  717.  
  718.     e. Using the list built during the ray casting we perform the
  719.        laborious task of translating and rotating the objects coordinates
  720.        relative to the players position and angle. This gives us final
  721.        coordinates that we can use to determine where to place the object
  722.        on the screen and what column to begin drawing the object. As we
  723.        begin drawing, the distance to the object is checked against the
  724.        distance to the corresponding wall. If the wall is closer, the
  725.        object is not drawn in that column. We don't need to check the
  726.        current object against all the other objects because they were
  727.        sorted by distance when found, they'll cover up objects farther
  728.        away by default.
  729.  
  730.     f. This process continues until all objects have been displayed in
  731.        the list. Each time the routine is called to draw the screen we
  732.        clear out the object seen flags and set the object count back
  733.        to zero. This allows us to start fresh with finding any objects
  734.        in our field of vision.
  735.  
  736.  
  737. ------------------------------------------------------------------------------
  738. VGA Mode X:
  739.  
  740.     1. During initial development it was decided to use the undocumented
  741.        VGA mode X (From Michael Aprash's articles in DDJ) which allow
  742.        off-screen drawing and page flipping. This worked reasonably well
  743.        on fast machines, but slower machines still had problems.
  744.  
  745.     2. The normal VGA 320x200 mode 13H has also been tried with good results,
  746.        but it is necessary to draw to a memory buffer (the way objects are
  747.        currently handled) and then copy the system memory buffer to the
  748.        screen. While it is a fast and straight forward process to move the
  749.        64,000 bytes from memory to video, it was still not fast enough to
  750.        use on slower machines.
  751.  
  752.     3. Experimenting has shown that it is possible to setup a screen mode
  753.        that gives 320x200 resolution but still has multiple pages, etc. This
  754.        cuts back on the number of screen writes needed (320 columns by 40
  755.        rows) and has shown a definite increase in performance.
  756.  
  757.     4. By way of the experimenting, the following files have been developed
  758.        to try various methods.
  759.  
  760.     ACKASM.ASM - This is the current in-use file which sets multiple plane
  761.              320x200 mode.
  762.  
  763.     ACKASM.VGA - This uses normal VGA 320x200 mode 13h
  764.  
  765.     ACKASM.240 - This uses 320x240 mode X
  766.  
  767.     These files can be used in place of one another to test the engine
  768.     in the various modes.
  769.  
  770.  
  771. ------------------------------------------------------------------------------
  772. Final comments:
  773.  
  774.     ACK3D has evolved over the past few weeks into a reasonable 3D graphics
  775.     engine and I am fairly pleased with the results thus far. There are,
  776.     however some annoying nuances that still plague the engine. These can be
  777.     summarized as follows, and in the words of most books "The rest is left
  778.     as an exercise for the reader" (Man I hate those words!).
  779.  
  780.     a. Accuracy at certain angles is still an issue, every so often a wall
  781.        sliver appears where it shouldn't, at least at the wrong height
  782.        anyway.
  783.  
  784.     b. Objects are getting clipped off the screen when thier centerpoint
  785.        is reached. They should be partially visible even if the center is
  786.        not.
  787.  
  788.     c. Collision detection is far from perfect. A better method needs to
  789.        be devised to handle coming up to walls and objects at any angle.
  790.  
  791.     d. Secret doors are mariginally working. They should be used with care
  792.        since the side walls of the moving door are invisible until the
  793.        secret door comes to a rest.
  794.  
  795.     e. Object movement is restricted to always turning right when bumping
  796.        into an obstacle. This needs to have more smarts built in to allow
  797.        moving objects to do things like chase the player, or run away, or
  798.        follow some predetermined path, etc.
  799.  
  800.     f. Other types of doors, besides secret and sliding doors needs to be
  801.        implemented. Future versions of the engine will handle a variety of
  802.        doors to deal with different uses of the engine.
  803.  
  804.     g. The goal actions need to be enhanced beyond displaying a screen that
  805.        says something on the order of YOU WON! I mean really....
  806.  
  807.  
  808.  
  809.     I hope this discussion, and the 3D engine itself, prove to be a good
  810.     starting point for your own creations. Many possibilities exist for this
  811.     type of environment ranging from underground dungeons, to space stations,
  812.     to art galleries. All thats needed is the right combination of graphics,
  813.     sound, and storyline.
  814.  
  815.  
  816.     Sincerely,
  817.     Lary Myers
  818.     5 Jacob Street
  819.     Ballston Lake, NY 12019
  820.  
  821.     CIS Account: 76004,1574
  822.  
  823.  
  824.  
  825.